// #include "initilizeGlobals.js"
var theBoard;
function onLoad(){
  window.focus();
  theBoard = kb.sym(document.location.href);
  if (isBBS) 
    bbsProtocolHandler.setCallback(document.location.href, initPostList);
  function filteredInitPostList(req){
  //dump("callback called: " + req.toSource() + '\n');
    if (!kb.statementsMatching(undefined, undefined, undefined,
                               kb.sym(req)).length)
      return true; //no information (eg. a redirection)
    initPostList();
    return false;
  }
  if (kb.any(undefined, SIOC('has_container'), theBoard))
    initPostList();
  sf.addCallback('done', refreshPostList);
  //else
  //sf.addCallback('done', filteredInitPostList);
}
refreshPostList.owner = window; 
//this helps the callback system know when the callback should be 
// inactivated (that is, when callback.owner.kb is not there)
window.addEventListener("DOMContentLoaded", onLoad, false);

function resizePost(){
  /*
  var post_header = document.getElementById("id_post_header");
  var post_content = document.getElementById("id_post_content");
  post_content.style.top = '80px';
  post_content.style.bottom = '0px';//(window.innerHeight)+"px";
  */
  
}

var selectedTr = null; // 被選取的文章 有一個●在它前面 然後他應該被顯示在螢幕中@@

// 鍵盤事件們
function onKeyPress(e){
  switch (e.keyCode){
  case 38: // UP
  case 40: // DOWN
    e.preventDefault();
    var candidate = (e.keyCode == 38) ? selectedTr.previousSibling :
                                        selectedTr.nextSibling;
    if (candidate){
      //刪掉postlist_table_selected這個class
      selectedTr.firstChild.className = 'postlist_table_number';
      candidate.firstChild.className += ' postlist_table_selected';
      selectedTr = candidate;
    }
    break;
  case 39: // RIGHT
    e.preventDefault();
    var articleToRead = selectedTr.about; //申成這個Tr的時候就加了的屬性
    if (articleToRead.termType === "bnode"){
      //代表尚未用Q查過這個文章的URI
      //var documentToLookupURI = document.location.href +
      //                        articleToRead.number.toString();
      //一個這個不會太久啦 xdd
      var documentToLookupURI = kb.any(articleToRead, RDFS('seeAlso')).uri;
      bbsProtocolHandler.lookUp(documentToLookupURI);
      
      function whenDone(){
        var articleWithURI = kb.canon(articleToRead);
        //function loadNext(){
          //@@目前這個文章會在後端被讀兩次
          window.location.assign(articleWithURI.uri);
          //}
        
        //以下可以讓 process -> fireCallback結束 才不會造成
        //process -> fireCallback -> whenDone -> window.location.assign
        // -> lookUpLater -> process 的無限遞迴
        //window.setInterval(loadNext, 0);
      }
      bbsProtocolHandler.setCallback(documentToLookupURI, whenDone);
    } else {
      //termType === "symbol" 已經知道文章的URI了
      var uriToRead = articleToRead.uri;
      if (uriToRead.startswith("http")){
        // http://www.wretch -> bbs://www.wretch (為了觸發onthewave)
        uriToRead = "bbs" + uriToRead.substr(4, uriToRead.length);
        //gConverterToPostList.convertResource(uriToRead, true);
        bbsProtocolHandler.tweakUriBack(uriToRead, true);
      }
      window.location.assign(uriToRead);
    }
    break;
  }
}

var numberDisplayed = 0; //已被顯示的文章個數
var gPosts = []; //所有被顯示的文章(symbol)的列
function initPostList(){
  resizePost();

  // post_list.html可以被用來顯示符合某些條件的文章，比如說在"八卦版推文大於20"
  // 這種條件(我最常用的xdd)，所以以下的code分成兩個部份。

  // Part I. 找出符合條件的文章
  var URIstr = document.location.href;
  var theBoard = kb.sym(URIstr);
  var thePostList = kb.each(undefined, SIOC('has_container'), theBoard);
  //加入這些文章的回文(想像一下blog的情形)
  for (var i = 0; i < thePostList.length; i++){
    var replies = kb.each(thePostList[i], SIOC('has_reply'));
    for (var j = 0; j < replies.length; j++){
      replies[j].reply_to = thePostList[i];
      thePostList.push(replies[j]);
      //@@消除重複
    }
  }
  numberDisplayed = thePostList.length;

  // Part II. 顯示這些文章

  // 條件裡面有"某個板限定"的時候 顯示板標、板主等等(PTT behavior)
  var theBoardTitle = kb.any(theBoard, DCTERMS('title')) ||
                      kb.any(theBoard, SIOC('name'));
  //我不喜歡SIOC('name')這個字彙@ tell #sioc
  if (!theBoardTitle) throw "此板雖然有文章但是沒有板標";

  var theBoardName = document.location.pathname.split('/')[1];
  var webAddressDisplay = theBoardTitle.value + " - 看板《" +
                          theBoardName + "》";
  document.title = webAddressDisplay;
  //screen.availHeight - document.getElementById("post_head").height;
  var boardOwnersStrings = kb.each(theBoard, SIOC('has_owner')).map(
                           function(x){return x.value});
  var boardOwnersDisplay = document.getElementById('boardOwners');
  if (boardOwnersStrings.length)
    boardOwnersDisplay.textContent = boardOwnersStrings.join("/");
  else if (isBBS)
    boardOwnersDisplay.textContent = "徵求中";
  document.getElementById('postlist_boardtitle').
           textContent = theBoardTitle;
  document.getElementById('boardName').
           textContent = theBoardName;

  var currentVisitors = kb.any(theBoard, BBS('currentVisitors')) ;
  var visitorIndicator = document.getElementById('currentVisitors');
  visitorIndicator.textContent = currentVisitors? 
                                 currentVisitors.value : 0; 
  //@ 預設人氣為0(這樣好嗎？)

  // 顯示文章們
  /// 排列文章 先是根據時間(假如有的話) 再來才是文章編號
  var theSortedPostList = thePostList.map(
                          function(x){return kb.canon(x);}); //@有空解釋
  /// 因為kb.any有點慢 把排列用的屬性cache起來
  for (var i=0;i<theSortedPostList.length;i++){
    var aArticle = theSortedPostList[i];
    var postTime = kb.any(aArticle, DCTERMS('created'));
    if (postTime)
      aArticle.postTime = ISO8601_parse(postTime.value); 
 
    aArticle.number = parseInt(kb.any(aArticle, 
                                      BBS('currentNumber'))); 
    //也可能沒有(NaN) 比如說從不知道哪裡得到某個文章URI(所以沒經過文章列表)讀入之後
    //就會不知道文章編號
  }
  function compareTimeThenNumber(articleA, articleB){
    if (articleA.number && articleB.number)
      return articleA.number - articleB.number;
    if (articleA.postTime && articleB.postTime)
      return articleA.postTime - articleB.postTime; //@沒想完整
    return 0; //一個只有時間一個只有編號
  }
  theSortedPostList.sort(compareTimeThenNumber); //這時候才排好

  /// 生成表格
  var postListTable = document.getElementById('postListTable');
  for (var i=0;i<theSortedPostList.length;i++){
    var aArticle = theSortedPostList[i];
    gPosts.push(aArticle);
    var tr = generateTrForArticle(aArticle);
    postListTable.appendChild(tr);
  }

  /// 設被選取 就那個●
  selectedTr = postListTable.lastChild; //預設為最後一個@@存cookie
  selectedTr.firstChild.className += " postlist_table_selected";

  /// 啟動鍵盤事件
  window.addEventListener('keypress', onKeyPress, false);

  //----------------------------------------------------------
  // 基本工作做完之後 讓後端做的事有很多選擇
  // 1. 補充這個板尚未出現的文章
  //    (補完 kb.each(undefined, SIOC('has_container'), theBoard))
  //    a. 不斷的上一頁
  //    b. 用"[" "]"找跟這頁相關的
  // 2. 先偷讀使用者可能會想看的文章 比如說有+ ~要消掉的
  // 3. 先偷用使用者常常會執行的搜尋 比說在八卦板找推文 > 20
  // 4. 補完一些未完成資訊 不完整的板主、文章標題名(有…的那些)

  // 1a.
  if (isBBS)
    conn.setBackendJobCallback(PAGEUP, updatePostList);
  // 2.
  if (!isBBS){
    for (var i = gPosts.length - 1; i >= 0; i--){
      var relatedDocs = kb.each(gPosts[i], RDFS('seeAlso'));
      for (var j = 0; j < relatedDocs.length; j++)
        sf.lookUpThing(relatedDocs[j]);
    }
  }

  //@暫時將捲軸鎖定在最下面
  var scrollDiv = postListTable.parentNode;
  scrollDiv.scrollTop = scrollDiv.scrollHeight - scrollDiv.offsetHeight+1;
  
}

function refreshPostList(aUri){
  if (kb.any(undefined, SIOC('has_container'), theBoard)){
    //清空文章列表
    var postListTable = document.getElementById('postListTable');
    var childs = postListTable.childNodes
      for (var i = childs.length -1; i >= 0; i--)
        postListTable.removeChild(childs[i]);
    
    initPostList();
  }
  return true; //keep refreshing
}

function updatePostList(){
  var URIstr = document.location.href;
  var theBoard = kb.sym(URIstr);
  var thePostList = kb.each(undefined, SIOC('has_container'), theBoard);
  thePostList = thePostList.slice(numberDisplayed); //處理未處理過的就行了
  numberDisplayed += thePostList.length;
  
  /// 排列文章 先是根據時間(假如有的話) 再來才是文章編號
  var theSortedPostList = thePostList.map(
                          function(x){return kb.canon(x);}); //@有空解釋
  /// 因為kb.any有點慢 把排列用的屬性cache起來
  for (var i=0;i<theSortedPostList.length;i++){
    var aArticle = theSortedPostList[i];
    var postTime = kb.any(aArticle, DCTERMS('created'));
    if (postTime)
      aArticle.postTime = ISO8601_parse(postTime.value); 
 
    aArticle.number = parseInt(kb.any(aArticle, 
                                      BBS('currentNumber'))); 
    //也可能沒有(NaN) 比如說從不知道哪裡得到某個文章URI(所以沒經過文章列表)讀入之後
    //就會不知道文章編號
  }
  
  function compareTimeThenNumber(articleA, articleB){ //倒過來排
    if (articleA.number && articleB.number)
      return - (articleA.number - articleB.number);
    if (articleA.postTime && articleB.postTime)
      return - (articleA.postTime - articleB.postTime); //@沒想完整
    return 0; //一個只有時間一個只有編號
  }
  theSortedPostList.sort(compareTimeThenNumber); //這時候才排好
  
  /// 補完表格
  var postListTable = document.getElementById('postListTable');
  for (var i=0;i<theSortedPostList.length;i++){
    var aArticle = theSortedPostList[i];
    var tr = generateTrForArticle(aArticle);
    postListTable.insertBefore(tr, postListTable.firstChild);
  }

  //@暫時將捲軸鎖定在最下面
  var scrollDiv = postListTable.parentNode;
  scrollDiv.scrollTop = scrollDiv.scrollHeight - scrollDiv.offsetHeight+1;  
}

// 顯示用的共用函數
function generateTrForArticle(aArticle){
  var aTr = document.createElement('tr');
  aTr.about = aArticle;
  var aNumberTd = document.createElement('td');
  aNumberTd.className = 'postlist_table_number';
  if (aArticle.number)
    aNumberTd.textContent = aArticle.number.toString();

  //@@ +=mM! 爆 XX
  var aTagTd = document.createElement('td');
  aTagTd.className = 'postlist_table_tag';
  var aScore = kb.any(aArticle, BBS('score'));
  if (aScore)
    aTagTd.textContent = aScore.value.toString();
  
  var aDateTd = document.createElement('td');
  aDateTd.className = 'postlist_table_date';
  var timeCreated = kb.any(aArticle, DCTERMS('created'));
  if (aArticle.postTime){
    var dateObj = aArticle.postTime;
    aDateTd.textContent = (dateObj.getMonth() + 1) + 
                          '/' + dateObj.getDate();
  }

  var aAuthorTd = document.createElement('td');
  aAuthorTd.className = 'postlist_table_author';
  var aAuthor = kb.any(aArticle, BBS('author_id'));
  if (aAuthor) //might not be available at this point, e.g. SIOC
    aAuthorTd.textContent = aAuthor.value;
  
  var aTitleTd = document.createElement('td');
  aTitleTd.className = 'postlist_table_title';
  //找到原文
  for (var originalArticle = aArticle; originalArticle.reply_to;
       originalArticle = originalArticle.reply_to);
  if (originalArticle.sameTerm(aArticle))
    aTitleTd.textContent = "□ " + kb.the(aArticle, 
                                          DCTERMS('title')).value;
  else
    aTitleTd.textContent = "R: " + kb.the(originalArticle, 
                                          DCTERMS('title')).value ;

  aTr.appendChild(aNumberTd);
  aTr.appendChild(aTagTd);
  aTr.appendChild(aDateTd);
  aTr.appendChild(aAuthorTd);
  aTr.appendChild(aTitleTd);  

  return aTr;
}


//boring utility

// time converting function copied from 
// <resource://gre/modules/ISO8601DateUtils.jsm>
const HOURS_TO_MINUTES = 60;
const MINUTES_TO_SECONDS = 60;
const SECONDS_TO_MILLISECONDS = 1000;
const MINUTES_TO_MILLISECONDS = MINUTES_TO_SECONDS * SECONDS_TO_MILLISECONDS;
const HOURS_TO_MILLISECONDS = HOURS_TO_MINUTES * MINUTES_TO_MILLISECONDS;
function ISO8601_parse(aDateString) {
    var dateString = aDateString;
    if (!dateString.match('-')) {
      // Workaround for server sending
      // dates such as: 20030530T11:18:50-08:00
      // instead of: 2003-05-30T11:18:50-08:00
      var year = dateString.slice(0, 4);
      var month = dateString.slice(4, 6);
      var rest = dateString.slice(6, dateString.length);
      dateString = year + "-" + month + "-" + rest;
    }

    var parts = dateString.match(/(\d{4})(-(\d{2,3}))?(-(\d{2}))?(T(\d{2}):(\d{2})(:(\d{2})(\.(\d+))?)?(Z|([+-])(\d{2}):(\d{2}))?)?/);

    // Here's an example of a W3C-DTF date string and what .match returns for it.
    //
    // date: 2003-05-30T11:18:50.345-08:00
    // date.match returns array values:
    //
    //   0: 2003-05-30T11:18:50-08:00,
    //   1: 2003,
    //   2: -05,
    //   3: 05,
    //   4: -30,
    //   5: 30,
    //   6: T11:18:50-08:00,
    //   7: 11,
    //   8: 18,
    //   9: :50,
    //   10: 50,
    //   11: .345,
    //   12: 345,
    //   13: -08:00,
    //   14: -,
    //   15: 08,
    //   16: 00

    // Create a Date object from the date parts.  Note that the Date
    // object apparently can't deal with empty string parameters in lieu
    // of numbers, so optional values (like hours, minutes, seconds, and
    // milliseconds) must be forced to be numbers.
    var date = new Date(parts[1], parts[3] - 1, parts[5], parts[7] || 0,
      parts[8] || 0, parts[10] || 0, parts[12] || 0);

    // We now have a value that the Date object thinks is in the local
    // timezone but which actually represents the date/time in the
    // remote timezone (f.e. the value was "10:00 EST", and we have
    // converted it to "10:00 PST" instead of "07:00 PST").  We need to
    // correct that.  To do so, we're going to add the offset between
    // the remote timezone and UTC (to convert the value to UTC), then
    // add the offset between UTC and the local timezone //(to convert
    // the value to the local timezone).

    // Ironically, W3C-DTF gives us the offset between UTC and the
    // remote timezone rather than the other way around, while the
    // getTimezoneOffset() method of a Date object gives us the offset
    // between the local timezone and UTC rather than the other way
    // around.  Both of these are the additive inverse (i.e. -x for x)
    // of what we want, so we have to invert them to use them by
    // multipying by -1 (f.e. if "the offset between UTC and the remote
    // timezone" is -5 hours, then "the offset between the remote
    // timezone and UTC" is -5*-1 = 5 hours).

    // Note that if the timezone portion of the date/time string is
    // absent (which violates W3C-DTF, although ISO 8601 allows it), we
    // assume the value to be in UTC.

    // The offset between the remote timezone and UTC in milliseconds.
    var remoteToUTCOffset = 0;
    if (parts[13] && parts[13] != "Z") {
      var direction = (parts[14] == "+" ? 1 : -1);
      if (parts[15])
        remoteToUTCOffset += direction * parts[15] * HOURS_TO_MILLISECONDS;
      if (parts[16])
        remoteToUTCOffset += direction * parts[16] * MINUTES_TO_MILLISECONDS;
    }
    remoteToUTCOffset = remoteToUTCOffset * -1; // invert it

    // The offset between UTC and the local timezone in milliseconds.
    var UTCToLocalOffset = date.getTimezoneOffset() * MINUTES_TO_MILLISECONDS;
    UTCToLocalOffset = UTCToLocalOffset * -1; // invert it
    date.setTime(date.getTime() + remoteToUTCOffset + UTCToLocalOffset);

    return date;
  }

